package org.myspringframework;

import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class ClassPathXmlApplicationContext implements ApplicationContext{
    /**
     * 存储bean的集合
     */
    private Map<String, Object> beanMap = new HashMap<>();

    /**
     * 该构造方法中，解析myspring.xml文件，创建所有的bean示例，并将bean实例存储到beanMap中
     * @param resource 配置文件路径（要求在类路径中）
     */
    public ClassPathXmlApplicationContext(String resource) {
        try {
            //dom4j解析xml文件的核心对象
            SAXReader reader = new SAXReader();
            //获取一个输入流，指向xml配置文件
            Document document = reader.read(ClassLoader.getSystemClassLoader().getResource(resource));
            //获取所有bean标签
            List<Node> beanNodes = document.selectNodes("//bean");
            //遍历bean标签
            beanNodes.forEach(beanNode -> {
                //向下转型的目的是使用element接口里更加丰富的方法
                Element beanElt = (Element) beanNode;
                //获取bean标签上的属性id、className
                String id = beanElt.attributeValue("id");
                String className = beanElt.attributeValue("class");
                //通过反射机制创建对象，将其放到Map集合中，提前曝光
                try {
                    Class<?> clazz = Class.forName(className);
                    Constructor<?> constructor = clazz.getDeclaredConstructor();
                    Object bean = constructor.newInstance();
                    //将bean曝光，加入map集合
                    beanMap.put(id, bean);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            });

            //再次重新把所有的bean标签遍历一次，这一次主要是给对象的属性赋值
            beanNodes.forEach(beanNode -> {
                try {
                    Element beanEl = (Element) beanNode;
                    String id = beanEl.attributeValue("id");
                    Object bean = beanMap.get(id);
                    Class<?> clazz = bean.getClass();
                    //获取bean标签下的所有property标签
                    List<Element> properties = beanEl.elements("property");
                    //遍历所有的属性标签
                    properties.forEach(propertyEl -> {
                        try {
                            //获取属性名
                            String propertyName = propertyEl.attributeValue("name");
                            //获取属性值
                            String propertyValue = propertyEl.attributeValue("value");
                            //获取属性类型
                            Class<?> propertyType = clazz.getDeclaredField(propertyName).getType();
                            //引用类型
                            String propertyRef = propertyEl.attributeValue("ref");
                            //获取set方法
                            String setMethodName = "set" + propertyName.toUpperCase().charAt(0) + propertyName.substring(1);
                            Method setMethod = clazz.getDeclaredMethod(setMethodName, propertyType);
                            //从xml中获取的值均为String类型，需要转换为对应类型
                            Object value = null;
                            //基本类型
                            if (propertyValue != null) {
                                String propertyTypeSimpleName = propertyType.getSimpleName();
                                switch (propertyTypeSimpleName) {
                                    case "byte": case "Byte":
                                        value = Byte.valueOf(propertyValue);
                                        break;
                                    case "short": case "Short":
                                        value = Short.valueOf(propertyValue);
                                        break;
                                    case "int": case "Integer":
                                        value = Integer.valueOf(propertyValue);
                                        break;
                                    case "long": case "Long":
                                        value = Long.valueOf(propertyValue);
                                        break;
                                    case "float": case "Float":
                                        value = Float.valueOf(propertyValue);
                                        break;
                                    case "double": case "Double":
                                        value = Double.valueOf(propertyValue);
                                        break;
                                    case "boolean": case "Boolean":
                                        value = Boolean.valueOf(propertyValue);
                                        break;
                                    case "char": case "Character":
                                        value = propertyValue.charAt(0);
                                        break;
                                    case "String":
                                        value = propertyValue;
                                        break;
                                }
                                setMethod.invoke(bean, value);
                            }
                            //引用类型
                            else if (propertyRef != null) {
                                setMethod.invoke(bean, beanMap.get(propertyRef));
                            }
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    });
                } catch (Exception e) {
                    e.printStackTrace();
                }

            });
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public Object getBean(String beanId) {
        return beanMap.get(beanId);
    }
}
